local utils = require('lin.utils')

vim.api.nvim_create_user_command(
    'JP',
    function(data)
        local args = data.fargs --[[ @as string[] ]]
        if #args == 0 then
            vim.cmd('cd %:p:h')
        else
            local rep = tonumber(args[1])
            if rep then
                vim.cmd('cd %:p:h' .. string.rep(':h', rep))
            end
        end
    end,
    { nargs = '?' }
)
vim.api.nvim_create_user_command(
    'Today',
    function()
        vim.cmd('normal a' .. vim.fn.strftime('%Y-%m-%d'))
    end,
    {}
)
vim.api.nvim_create_user_command(
    'Qa',
    function()
        local api = vim.api
        local diff_type_map = {
            a = ' line(s) added: ',
            c = ' line(s) changed to: ',
            d = ' line(s) deleted after: '
        }
        local actually_modified = {}
        utils.loaded_bufs_do(function(buf)
            if not vim.bo[buf].buflisted
                or not vim.bo[buf].modified
                or vim.bo[buf].readonly then
                api.nvim_buf_delete(buf, { force = true })
                return
            end

            if api.nvim_buf_get_name(buf) == '' then
                if vim.bo[buf].modified then
                    table.insert(actually_modified, {
                        bufnr = buf,
                        lnum = 1,
                        type = 'I',
                        text = 'Modified un-named file'
                    })
                else
                    api.nvim_buf_delete(buf, {})
                end

                return
            end

            ---@type string
            local diff_out = api.nvim_buf_call(buf, function()
                return api.nvim_exec2('write !diff % -', { output = true })
                    .output
            end)
            -- the first character is always newline(\n)
            diff_out = diff_out:sub(2)
            if diff_out == '' then
                api.nvim_buf_delete(buf, { force = true })
                return
            end

            local typ, s, e
            for line_range in diff_out:gmatch('[0-9,]+([cad][0-9,]+)') do
                typ = line_range:sub(1, 1)
                line_range = line_range:sub(2)

                local i, _ = line_range:find(',')
                if i then
                    s, e = tonumber(line_range:sub(1, i - 1)),
                        tonumber(line_range:sub(i + 1))
                else
                    s = tonumber(line_range)
                    e = s
                end
                assert(s, "expect number")
                assert(e, "expect number")
                table.insert(actually_modified, {
                    bufnr = buf,
                    lnum = s,
                    end_lnum = e,
                    type = 'I',
                    text = tostring(e - s + 1) .. diff_type_map[typ] ..
                        table.concat(
                            api.nvim_buf_get_lines(buf, s - 1, e, false),
                            '[↵]'
                        )
                })
            end
        end)
        if #actually_modified > 0 then
            vim.fn.setqflist(actually_modified, 'r')
            vim.cmd('copen')
        else
            vim.cmd('quit')
        end
    end,
    {}
)

local all_themes = require('lin.utils').all_themes()
local all_themes_count = #all_themes
local try_avoid_first = all_themes_count / 5 * 4
local function random_theme()
    math.randomseed(os.time())
    local chosen = math.random(all_themes_count)
    -- for _ = 1, 2 do
    --     if chosen > try_avoid_first then break end
    --     chosen = math.random(all_themes_count)
    -- end
    return all_themes[chosen]
end

vim.api.nvim_create_user_command(
    'Looks',
    function(data)
        if #data.fargs == 0 then
            local chosen = random_theme()
            utils.looks(chosen, chosen:find('-light') == nil, false)
        elseif vim.tbl_contains(all_themes, data.fargs[1]) then
            ---@type string
            local chosen = data.fargs[1]
            local dark
            if data.fargs[2] then
                dark = data.fargs[2] ~= '0'
            else
                dark = chosen:find('-light') == nil
            end
            utils.looks(data.fargs[1], dark, false)
        end
    end,
    {
        nargs = '*',
        complete = function(arglead, _, _)
            ---@type string
            local prefix = arglead
            return utils.select_from_prefix(prefix, all_themes)
        end
    }
)

vim.api.nvim_create_user_command(
    'SaveLook',
    function(data)
        local theme = vim.api.nvim_cmd({ cmd = 'colorscheme' }, { output = true })
        local bg = vim.go.bg

        local save_for = 'day';
        if #data.fargs == 0 then
            local hour = tonumber(os.date('%H'))
            save_for = (hour >= 6 and hour <= 17) and 'day' or 'night'
        elseif data.fargs[1] ~= 'day' then
            save_for = 'night'
        end

        local conf

        local fname = ScriptRoot .. 'looks-good.conf'
        local f = io.open(fname, 'r+')
        if f then
            conf = vim.json.decode(f:read('*a'))
            conf[save_for].theme = theme
            conf[save_for].bg = bg
            f:close()
        else
            conf = { day = { theme = theme, bg = 'light' }, night = { theme = theme, bg = 'dark' } }
        end
        f = io.open(fname, 'w+')
        if f then
            f:write(vim.json.encode(conf))
            f:close()
        end
    end,
    {
        nargs = '?',
        complete = function(arglead, _, _)
            ---@type string
            local prefix = arglead
            return utils.select_from_prefix(prefix, { 'day', 'night' })
        end
    }
)

vim.api.nvim_create_user_command(
    'LeadSpaceGroupSetLen',
    function(data)
        local bef, aft
        if #data.fargs == 1 then
            bef, aft = vim.opt.tabstop, tonumber(data.fargs[1])
            if aft then
                vim.opt.tabstop = aft
                vim.opt.shiftwidth = aft
            end
        elseif #data.fargs == 2 then
            bef, aft = tonumber(data.fargs[1]), tonumber(data.fargs[2])
        end
        if bef and aft then
            utils.leading_seq_replace(
                string.rep(' ', bef),
                string.rep(' ', aft)
            )
        else
            print('Usage: ' .. data.name .. ' [<old_space_group_len>] <new_space_group_len>')
        end
    end,
    { nargs = '+' }
)

vim.api.nvim_create_user_command(
    'TabSetLen',
    function(data)
        local val = tonumber(data.fargs[1])
        if val then
            vim.opt.tabstop = val
            vim.opt.shiftwidth = val
        else
            print('Usage: ' .. data.name .. ' <new_tab_len>')
        end
    end,
    { nargs = 1 }
)

vim.api.nvim_create_user_command(
    'TabWelcome',
    function(data)
        vim.opt.expandtab = false
        if data.fargs[1] == 'hide' then
            vim.opt.listchars:append('tab:  ')
        elseif data.fargs[1] == 'show' then
            vim.opt.listchars:append('tab:›·')
        end
        if data.bang then
            utils.leading_seq_replace(
                string.rep(' ', vim.opt.tabstop),
                '\t'
            )
            -- transform all tab-length-long sequence of spaces:
            -- vim.cmd('retab!')
            -- relies on 'coreutils':
            -- vim.cmd('%!unexpand --first-only --tabs=' .. tostring(tab_len))
        end
    end,
    {
        nargs = 1,
        bang = true,
        complete = function(arglead, _, _)
            ---@type string
            local prefix = arglead
            return utils.select_from_prefix(prefix, { 'show', 'hide' })
        end
    }
)

vim.api.nvim_create_user_command(
    'TabBye',
    function(data)
        vim.opt.expandtab = true
        -- Better let tabs show themself if you don't want to work with tabs:
        vim.opt.listchars:append('tab:»·')
        if data.bang then
            utils.leading_seq_replace(
                '\t',
                string.rep(' ', vim.opt.tabstop)
            )
        end
    end,
    { nargs = 0, bang = true }
)

vim.api.nvim_create_user_command(
    'LspHlSymRef',
    function(data)
        if #data.fargs > 0 and data.fargs[1] == 'clr' then
            vim.lsp.buf.clear_references()
        else
            vim.lsp.buf.document_highlight()
        end
    end,
    {
        nargs = '?',
        complete = function()
            return { 'clr' }
        end
    }
)
vim.api.nvim_create_user_command(
    'LspInlayHintToggle',
    function(data)
        local filter = {}
        if data.fargs[1] == 'buf' then
            filter = { bufnr = 0 }
        end
        vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled(filter), filter)
    end,
    {
        nargs = 1,
        complete = function(arglead, _, _)
            ---@type string
            local prefix = arglead
            return utils.select_from_prefix(prefix, { 'buf', 'all' })
        end
    }
)
